package com.hsd.znsh.config;

import com.github.qcloudsms.SmsSingleSender;
import com.hsd.znsh.properties.HibernateProperties;
import com.hsd.znsh.properties.YsProperties;
import com.hsd.znsh.security.session.MyExpiredSessionStrategy;
import com.hsd.znsh.security.session.MyInvalidSessionStrategy;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.apache.tomcat.jdbc.pool.DataSource;
import com.hsd.znsh.properties.SecurityProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.servlet.MultipartConfigElement;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableConfigurationProperties({SecurityProperties.class, HibernateProperties.class, YsProperties.class})
public class ZnshBeanConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public RequestCache requestCache(){
        return new HttpSessionRequestCache();
    }

    @Bean
    public RedirectStrategy redirectStrategy() {
        return new DefaultRedirectStrategy();
    }

    @Bean
    public LocalSessionFactoryBean sessionFactory(DataSource dataSource, HibernateProperties hibernateProperties) {
        LocalSessionFactoryBean sessionFactory=new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setPackagesToScan("com.hsd.znsh.entity");
        Properties properties=new Properties();
        properties.setProperty("hibernate.dialect",hibernateProperties.getDialect());
        properties.setProperty("hibernate.show_sql",hibernateProperties.getShow_sql());
        properties.setProperty("hibernate.format_sql",hibernateProperties.getFormat_sql());
        properties.setProperty("hibernate.hbm2ddl.auto",hibernateProperties.getHbm2ddl());
        sessionFactory.setHibernateProperties(properties);
        return sessionFactory;
    }

    @Bean
    public MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        factory.setMaxFileSize(10*1024L * 1024L);
        return factory.createMultipartConfig();
    }

    @Bean
    public InvalidSessionStrategy invalidSessionStrategy(SecurityProperties securityProperties){
        return new MyInvalidSessionStrategy(securityProperties.getBrowser().getSession().getSessionInvalidUrl());
    }

    @Bean
    public SessionInformationExpiredStrategy sessionInformationExpiredStrategy(SecurityProperties securityProperties){
        return new MyExpiredSessionStrategy(securityProperties.getBrowser().getSession().getSessionInvalidUrl());
    }

    @Bean
    public SmsSingleSender smsSingleSender(SecurityProperties securityProperties){
        int appId=securityProperties.getCode().getSms().getAppId();
        String appKey=securityProperties.getCode().getSms().getAppKey();
        SmsSingleSender sender = new SmsSingleSender(appId, appKey);
        return sender;
    }

    @Bean
    public RestTemplate restTemplate() throws Exception {
        TrustStrategy acceptingTrustStrategy=(X509Certificate[] chain, String authType)-> true;
        SSLContext sslContext= SSLContexts.custom().loadTrustMaterial(null,acceptingTrustStrategy).build();

        HostnameVerifier verifier = (String hostname, SSLSession session)-> true;
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, verifier);
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory();

        requestFactory.setHttpClient(httpClient);
        return new RestTemplate(requestFactory);
    }

    @Bean
    public SessionRegistry sessionRegistry(){
        return new SessionRegistryImpl();
    }

    @Bean
    public ExecutorService threadPool(){
        return Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 5, r-> {
            Thread thread=new Thread(r);
            thread.setDaemon(true);
            return thread;
        });
    }

}
